﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace Lilium.LParse.CSharp
{
    /// <summary>
    /// C# lexical parsers.
    /// </summary>
    /// <remarks>
    /// Based on grammar reference () by C# Language Specification 3.0 (http://msdn.microsoft.com/en-us/library/aa664812(VS.71).aspx)
    /// </remarks>
    public static class CSharpParsers
    {
        static CSharpParsers()
        {
            EmptyDigitsCollection = new int[0];
            EmptyExponentPart = new ExponentPart(CSharp.Sign.Plus, EmptyDigitsCollection);

            #region B.1.1 Line terminators

            NewLine = ParserCombinators.Choise(new[] {
                from cr in TextParsers.Char('\u000D').Select(ch => ch.ToString())                   // Carriage return character (U+000D)
                from lf in TextParsers.Char('\u000A').Select(ch => ch.ToString()).Optional(null)    // Optionally followed by the line feed character (U+000A)
                select cr + lf,

                from ch in TextParsers.Char('\u000A') select ch.ToString(),                         // Line feed character (U+000A)
                from ch in TextParsers.Char('\u2028') select ch.ToString(),                         // Line separator character (U+2028)
                from ch in TextParsers.Char('\u2029') select ch.ToString(),                         // Paragraph separator character (U+2029)
            });

            #endregion

            #region B.1.2 Comments

            NotSlash = TextParsers.Satisfy(c => c != '/', new[] { "not-slash" });

            NotAsterisk = TextParsers.Satisfy(c => c != '*', new[] { "not-asterisk" });

            DelimitedCommentCharacter =
                NotAsterisk.Or(
                (from asterisk in TextParsers.Char('*')
                 from notSlash in NotSlash
                 select notSlash).Try("asterisk followed by not-slash"));

            DelimitedCommentCharacters = DelimitedCommentCharacter.Many1();

            DelimitedComment =
                from openSeq in TextParsers.Word("/*")
                from comment in DelimitedCommentCharacters.Optional("")
                from closeSeq in TextParsers.Word("*/")
                select comment;

            NewLineCharacter = TextParsers.Satisfy(IsNewLineCharacter, NewLineCharacterExpectations);

            InputCharacter = TextParsers.Satisfy(ch => !IsNewLineCharacter(ch), NotNewLineCharacterExpectations);

            InputCharacters = InputCharacter.Many1();

            SingleLineComment =
                from openSeq in TextParsers.Word("//")
                from comment in InputCharacters.Optional("")
                select comment;

            Comment = SingleLineComment.Try("single-line-comment").Or(DelimitedComment);

            #endregion

            #region B.1.3 White space

            WhiteSpace = ParserCombinators.Choise(new[] {
                TextParsers.Category(UnicodeCategory.SpaceSeparator),   // Any character with Unicode class Zs
                TextParsers.Char('\u0009'),                             // Horizontal tab character (U+0009)
                TextParsers.Char('\u000B'),                             // Vertical tab character (U+000B)
                TextParsers.Char('\u000C'),                             // Form feed character (U+000C)
            });

            #endregion

            #region B.1.5 Unicode character escape sequences

            HexDigit = ParserCombinators.Choise(new[] {
                from ch in TextParsers.Range('0', '9') select ch - '0',
                from ch in TextParsers.Range('a', 'f') select ch - 'a' + 10,
                from ch in TextParsers.Range('A', 'F') select ch - 'A' + 10 });

            UnicodeEscapeSequence =
                from seqPrefix in TextParsers.Char('\\')
                from ch in
                    (from _ in TextParsers.Char('u')
                     from c in HexDigit.Fold1Count(4, (a, d) => a * 16 + d)
                     select (char)c).Or(
                     from _ in TextParsers.Char('U')
                     from c in HexDigit.Fold1Count(8, (a, d) => a * 16 + d)
                     select (char)c)
                select ch;

            #endregion

            #region B.1.8 Literals

            BooleanLiteral =
                (from _ in TextParsers.TryWord("true") select true).Or
                (from _ in TextParsers.Word("false") select false);

            DecimalDigit = from ch in TextParsers.Range('0', '9') select ch - '0';

            DecimalDigits = DecimalDigit.Many1("decimal digit");

            var ulSuffixes = new[] { "UL", "Ul", "uL", "ul", "LU", "Lu", "lU", "lu" };

            IntegerTypeSuffix = ParserCombinators.Choise(new[] {
                    from _ in TextParsers.TryOneOf(ulSuffixes) select CSharp.IntegerTypeSuffix.ULong,

                    from _ in TextParsers.OneOf("Uu") select CSharp.IntegerTypeSuffix.UInt,
                    from _ in TextParsers.OneOf("Ll") select CSharp.IntegerTypeSuffix.Long,
                });

            DecimalIntegerLiteral =
                from digits in DecimalDigits
                from suffix in IntegerTypeSuffix.Optional(CSharp.IntegerTypeSuffix.None)
                select new IntegerLiteral(digits, suffix);

            // HexDigit moved to  C.1.5 

            HexDigits = HexDigit.Many1("hexadecimal digit");

            HexadecimalIntegerLiteral =
                from _0 in TextParsers.Char('0')
                from _1 in TextParsers.OneOf("xX")
                from digits in HexDigits
                from suffix in IntegerTypeSuffix.Optional(CSharp.IntegerTypeSuffix.None)
                select new IntegerLiteral(digits, suffix);

            Sign =
                (from _ in TextParsers.Char('+') select CSharp.Sign.Plus)
                 .Or
                (from _ in TextParsers.Char('-') select CSharp.Sign.Minus);

            ExponentPart =
                from _ in TextParsers.OneOf("eE")
                from sign in Sign.Optional(CSharp.Sign.Plus)
                from digits in DecimalDigits
                select new ExponentPart(sign, digits);

            RealTypeSuffix = ParserCombinators.Choise(new[] {
                from f in TextParsers.OneOf("fF") select CSharp.RealTypeSuffix.Float,
                from d in TextParsers.OneOf("dD") select CSharp.RealTypeSuffix.Double,
                from m in TextParsers.OneOf("mM") select CSharp.RealTypeSuffix.Money,
            });

            RealLiteral = ParserCombinators.Choise(new[] {
                (from integral in DecimalDigits 
                 from _ in TextParsers.Char('.')
                 from fractional in DecimalDigits 
                 from exponentPart in ExponentPart.Optional(EmptyExponentPart)
                 from realTypeSuffix in RealTypeSuffix.Optional(CSharp.RealTypeSuffix.None)
                 select new RealLiteral(integral, fractional, exponentPart, realTypeSuffix)).Try(),
                
                 from _ in TextParsers.Char('.') // т.к. начинается с точки, комбинатор try не нужен
                 from fractional in DecimalDigits 
                 from exponentPart in ExponentPart.Optional(EmptyExponentPart)
                 from realTypeSuffix in RealTypeSuffix.Optional(CSharp.RealTypeSuffix.None)
                 select new RealLiteral(EmptyDigitsCollection, fractional, exponentPart, realTypeSuffix),

                (from integral in DecimalDigits 
                 from exponentPart in ExponentPart
                 from realTypeSuffix in RealTypeSuffix.Optional(CSharp.RealTypeSuffix.None)
                 select new RealLiteral(integral, EmptyDigitsCollection, exponentPart, realTypeSuffix)).Try(),

                 from integral in DecimalDigits 
                 from realTypeSuffix in RealTypeSuffix
                 select new RealLiteral(integral, EmptyDigitsCollection, EmptyExponentPart, realTypeSuffix)
                });

            SingleCharacter = TextParsers.Satisfy(IsSingleCharacter, SingleCharacterExpectations);

            SimpleEscapeSequence =
                from pref in TextParsers.Char('\\')
                from ch in
                    ParserCombinators.Choise(
                        TextParsers.Char('\''),
                        TextParsers.Char('"'),
                        TextParsers.Char('\\'),
                        from _ in TextParsers.Char('0') select '\0',
                        from _ in TextParsers.Char('a') select '\a',
                        from _ in TextParsers.Char('b') select '\b',
                        from _ in TextParsers.Char('f') select '\f',
                        from _ in TextParsers.Char('n') select '\n',
                        from _ in TextParsers.Char('r') select '\r',
                        from _ in TextParsers.Char('t') select '\t',
                        from _ in TextParsers.Char('v') select '\v')
                select ch;

            HexadecimalEscapeSequence =
                from prefix in TextParsers.Word("\\x")
                from c in HexDigit.Fold1Count(1, 4, (a, d) => a * 16 + d)
                select (char)c;

            Character = ParserCombinators.Choise(
                SingleCharacter,
                SimpleEscapeSequence.Try("simple escape sequence"),
                HexadecimalEscapeSequence.Try("hexadecimal escape sequence"),
                UnicodeEscapeSequence);

            CharLiteral =
                from _1 in TextParsers.Char('\'')
                from ch in Character
                from _2 in TextParsers.Char('\'')
                select ch;

            SingleRegularStringLiteralCharacter = TextParsers.Satisfy(IsSingleRegularStringLiteralCharacter, SingleRegularStringLiteralCharacterExpectations);

            RegularStringLiteralCharacter = ParserCombinators.Choise(
                SingleRegularStringLiteralCharacter,
                SimpleEscapeSequence.Try("simple escape sequence"),
                HexadecimalEscapeSequence.Try("hexadecimal escape sequence"),
                UnicodeEscapeSequence);

            RegularStringLiteralCharacters = RegularStringLiteralCharacter.Many1();

            RegularStringLiteral =
                from _1 in TextParsers.Char('\"')
                from str in RegularStringLiteralCharacter.Many()
                from _2 in TextParsers.Char('\"')
                select str;

            SingleVerbatimStringLiteralCharacter = TextParsers.Not('"');

            QuoteEscapeSequence =
                from _ in TextParsers.Word("\"\"")
                select '"';

            VerbatimStringLiteralCharacter = SingleVerbatimStringLiteralCharacter.Or(QuoteEscapeSequence);

            VerbatimStringLiteralCharacters = VerbatimStringLiteralCharacter.Many1();

            VerbatimStringLiteral =
                from _1 in TextParsers.Word("@\"")
                from str in VerbatimStringLiteralCharacters
                from _2 in TextParsers.Char('"')
                select str;

            StringLiteral =
                RegularStringLiteral.Or(VerbatimStringLiteral); // не используем try, т.к. стринги различаются без заглядывания вперед.

            NullLiteral = TextParsers.Word("null");

            #endregion

            #region B.1.6 Identifiers

            LetterCharacter = CharacterOrUnicodeEscapeSequence(IsLetterCharacter, LetterCharacterExpectations);
            CombiningCharacter = CharacterOrUnicodeEscapeSequence(IsCombiningCharacter, CombiningCharacterExpectations);
            DecimalDigitCharacter = CharacterOrUnicodeEscapeSequence(IsDecimalDigitCharacter, DecimalDigitCharacterExpectations);
            ConnectingCharacter = CharacterOrUnicodeEscapeSequence(IsConnectingCharacter, ConnectingCharacterExpectations);
            FormattingCharacter = CharacterOrUnicodeEscapeSequence(IsFomattingCharacter, FomattingCharacterExpectations);

            IdentifierStartCharacter = LetterCharacter.Try("letter character").Or(TextParsers.Char('_'));

            IdentifierPartCharacter = ParserCombinators.Choise(
                LetterCharacter.Try("letter character"),
                DecimalDigitCharacter.Try("decimal digit character"),
                ConnectingCharacter.Try("connecting character"),
                CombiningCharacter.Try("combining character"),
                FormattingCharacter.Try("formatting character"));

            IdentifierOrKeyword =
                from startCharacter in IdentifierStartCharacter
                from identifier in IdentifierPartCharacter.FoldMany1((sb, ch) => sb.Append(ch), new StringBuilder(startCharacter.ToString()))
                select identifier.ToString();

            AvailableIdentifier = IdentifierOrKeyword.Validate(s => Array.BinarySearch(Keywords, s) <= 0);

            Identifier =
                AvailableIdentifier.Try("available identifier")
                .Or(from _ in TextParsers.Char('@')
                    from identifierOrKeyword in IdentifierOrKeyword
                    select identifierOrKeyword);

            #endregion

            #region B.1.7 Keywords

            Keyword = TextParsers.TryOneOf(Keywords);

            #endregion

            #region B.1.9 Operators and punctuators

            OperatorOrPunctuator = TextParsers.TryOneOf(OperatorsAndPunctuators);

            RightShift = TextParsers.Word(">>");

            RightShiftAssignment = TextParsers.Word(">>=");

            #endregion

            #region B.1.10 Pre-processing directives

            PPNewLine =
                from _1 in WhiteSpace.SkipMany1()
                from _2 in SingleLineComment.Optional()
                from _3 in NewLine
                select _1 + _2 + _3;

            ConditionalSymbol = IdentifierOrKeyword.Satisfy(w => w != "true" && w != "false");

            WarningAction =
                (from _ in TextParsers.Word("disable") select CSharp.WarningAction.Disable)
                .Or(
                from _ in TextParsers.Word("restore") select CSharp.WarningAction.Restore);

            WarningList = DecimalDigit.Fold1Many1((accum, value) => accum * 10 + value)
                .SepBy1(
                    from _1 in WhiteSpace.SkipMany()
                    from _2 in TextParsers.Char(',')
                    from _3 in WhiteSpace.SkipMany()
                    select (char)0);

            PragmaWarningBody =
                from _1 in TextParsers.Word("warning")
                from _2 in WhiteSpace.SkipMany1()
                from warningAction in WarningAction
                from warningList in
                    (from __1 in WhiteSpace.SkipMany1()
                     from warningList in WarningList
                     select warningList).Optional(EmptyDigitsCollection)
                select new PragmaWarningBody(warningAction, warningList);

            PragmaBody = PragmaWarningBody;



//pp-pragma:
//whitespaceopt   #   whitespaceopt   pragma   whitespace   pragma-body   pp-new-line


            #endregion
        }

        #region B.1.1 Line terminators

        public static readonly Parser<char, string> NewLine;

        #endregion

        #region B.1.2 White space

        public static readonly Parser<char, char> WhiteSpace;

        #endregion

        #region B.1.3 Comments

        public static readonly Parser<char, string> Comment;

        public static readonly Parser<char, string> SingleLineComment;

        public static readonly Parser<char, string> InputCharacters;

        public static readonly Parser<char, char> InputCharacter;

        #region NewLineCharacter

        public static readonly Parser<char, char> NewLineCharacter;

        private static readonly object[] NewLineCharacterExpectations = TextLabels.GetExceptedChars("\u000D\u000A\u2028\u2029").ToArray();
        private static readonly object[] NotNewLineCharacterExpectations = TextLabels.GetNoneOfExceptedChars("\u000D\u000A\u2028\u2029").ToArray();

        private static bool IsNewLineCharacter(char ch)
        {
            switch (ch)
            {
                case '\u000D':
                case '\u000A':
                case '\u2028':
                case '\u2029':
                    return true;
                default:
                    return false;
            }
        }

        #endregion

        public static readonly Parser<char, string> DelimitedComment;

        public static readonly Parser<char, string> DelimitedCommentCharacters;

        public static readonly Parser<char, char> DelimitedCommentCharacter;

        public static readonly Parser<char, char> NotAsterisk;

        public static readonly Parser<char, char> NotSlash;

        #endregion

        #region B.1.5 Unicode character escape sequences

        /// <summary>
        /// Unicode escape sequence parser.
        /// </summary>
        /// <returns></returns>
        public static readonly Parser<char, char> UnicodeEscapeSequence;

        public static Parser<char, char> CharacterOrUnicodeEscapeSequence(Func<char, bool> predicate, IEnumerable<object> exceptedValues)
        {
            return TextParsers.Satisfy(predicate, exceptedValues)
                .Or(UnicodeEscapeSequence.Validate(predicate, exceptedValues));
        }

        #endregion

        #region B.1.6 Identifiers

        /// <summary>
        /// Identifier
        /// </summary>
        /// <remarks>
        /// available-identifier
        /// @identifier-or-keyword
        /// </remarks>
        public static readonly Parser<char, string> Identifier;

        /// <summary>
        /// Available identifier
        /// </summary>
        /// <remarks>
        /// An identifier-or-keyword that is not a keyword
        /// </remarks>
        public static readonly Parser<char, string> AvailableIdentifier;

        /// <summary>
        /// Identifier or keyword
        /// </summary>
        /// <remarks>
        /// identifier-start-character   identifier-part-characters* opt
        /// </remarks>
        public static readonly Parser<char, string> IdentifierOrKeyword;

        /// <summary>
        /// Identifier start character
        /// </summary>
        /// <remarks>
        /// letter-character
        /// _ (the underscore character U+005F)
        /// </remarks>
        public static readonly Parser<char, char> IdentifierStartCharacter;

        /// <summary>
        /// Identifier part character
        /// </summary>
        /// <remarks>
        /// letter-character
        /// decimal-digit-character
        /// connecting-character
        /// combining-character
        /// formatting-character
        /// </remarks>
        public static readonly Parser<char, char> IdentifierPartCharacter;

        #region LetterCharacter

        /// <summary>
        /// Letter character.
        /// </summary>
        /// <remarks>
        /// A Unicode character of classes Lu, Ll, Lt, Lm, Lo, or Nl 
        /// A unicode-escape-sequence representing a character of classes Lu, Ll, Lt, Lm, Lo, or Nl
        /// </remarks>
        public static readonly Parser<char, char> LetterCharacter;

        private static readonly object[] LetterCharacterExpectations = TextLabels.GetExceptedCategories(
            UnicodeCategory.UppercaseLetter,
            UnicodeCategory.LowercaseLetter,
            UnicodeCategory.TitlecaseLetter,
            UnicodeCategory.ModifierLetter,
            UnicodeCategory.OtherLetter,
            UnicodeCategory.LetterNumber).ToArray();

        private static bool IsLetterCharacter(char ch)
        {
            switch (char.GetUnicodeCategory(ch))
            {
                case UnicodeCategory.UppercaseLetter:
                case UnicodeCategory.LowercaseLetter:
                case UnicodeCategory.TitlecaseLetter:
                case UnicodeCategory.ModifierLetter:
                case UnicodeCategory.OtherLetter:
                case UnicodeCategory.LetterNumber:
                    return true;
                default:
                    return false;
            }
        }

        #endregion

        #region CombiningCharacter

        /// <summary>
        /// Combining character
        /// </summary>
        /// <remarks>
        /// A Unicode character of classes Mn or Mc 
        /// A unicode-escape-sequence representing a character of classes Mn or Mc
        /// </remarks>
        public static readonly Parser<char, char> CombiningCharacter;

        private static readonly object[] CombiningCharacterExpectations = TextLabels.GetExceptedCategories(UnicodeCategory.NonSpacingMark, UnicodeCategory.SpacingCombiningMark).ToArray();

        private static bool IsCombiningCharacter(char ch)
        {
            switch (char.GetUnicodeCategory(ch))
            {
                case UnicodeCategory.NonSpacingMark:
                case UnicodeCategory.SpacingCombiningMark:
                    return true;
                default:
                    return false;
            }
        }

        #endregion

        #region DecimalDigitCharacter

        /// <summary>
        /// Decimal digit character
        /// </summary>
        /// <remarks>
        /// A Unicode character of the class Nd 
        /// A unicode-escape-sequence representing a character of the class Nd
        /// </remarks>
        public static readonly Parser<char, char> DecimalDigitCharacter;

        private static readonly object[] DecimalDigitCharacterExpectations = TextLabels.GetExceptedCategory(UnicodeCategory.DecimalDigitNumber).ToArray();

        private static bool IsDecimalDigitCharacter(char ch)
        {
            return char.GetUnicodeCategory(ch) == UnicodeCategory.DecimalDigitNumber;
        }

        #endregion

        #region ConnectingCharacter

        /// <summary>
        /// Connecting character
        /// </summary>
        /// <remarks>
        /// A Unicode character of the class Pc 
        /// A unicode-escape-sequence representing a character of the class Pc
        /// </remarks>
        public static readonly Parser<char, char> ConnectingCharacter;

        private static readonly object[] ConnectingCharacterExpectations = TextLabels.GetExceptedCategory(UnicodeCategory.ConnectorPunctuation).ToArray();

        private static bool IsConnectingCharacter(char ch)
        {
            return char.GetUnicodeCategory(ch) == UnicodeCategory.ConnectorPunctuation;
        }

        #endregion

        #region FormattingCharacter

        /// <summary>
        /// Formatting character
        /// </summary>
        /// <remarks>
        /// A Unicode character of the class Cf 
        /// A unicode-escape-sequence representing a character of the class Cf
        /// </remarks>
        public static readonly Parser<char, char> FormattingCharacter;

        private static readonly object[] FomattingCharacterExpectations = TextLabels.GetExceptedCategory(UnicodeCategory.Format).ToArray();

        private static bool IsFomattingCharacter(char ch)
        {
            return char.GetUnicodeCategory(ch) == UnicodeCategory.Format;
        }

        #endregion

        #endregion

        #region B.1.7 Keywords

        public static Parser<char, string> Keyword;

        public static string[] Keywords = new[] { 
            "abstract",     "as",	        "base",     "bool", 	    "break", 	    "byte",	        "case",
            "catch",        "char",	        "checked",	"class",	    "const",	    "continue",	    "decimal",
            "default",	    "delegate",	    "do",	    "double",	    "else",	        "enum",	        "event",
            "explicit",	    "extern",	    "false",	"finally",	    "fixed",	    "float",	    "for",
            "foreach",	    "goto",	        "if",	    "implicit",	    "in",	        "int",	        "interface",
            "internal",	    "is",	        "lock",	    "long",	        "namespace",	"new",	        "null",
            "object",	    "operator",	    "out",	    "override",	    "params",	    "private",	    "protected",
            "public",	    "readonly",	    "ref",	    "return",	    "sbyte",	    "sealed",	    "short",
            "sizeof",	    "stackalloc",	"static",	"string",	    "struct",	    "switch",	    "this",
            "throw",	    "true",	        "try",	    "typeof",	    "uint",	        "ulong",	    "unchecked",
            "unsafe",	    "ushort",	    "using",	"virtual",	    "void",	        "volatile",	    "while"     };

        #endregion

        #region B.1.8 Literals

        public static readonly Parser<char, bool> BooleanLiteral;

        public static readonly Parser<char, IntegerLiteral> DecimalIntegerLiteral;

        public static readonly Parser<char, ICollection<int>> DecimalDigits;

        public static readonly Parser<char, int> DecimalDigit;

        public static readonly Parser<char, IntegerTypeSuffix> IntegerTypeSuffix;

        public static readonly Parser<char, IntegerLiteral> HexadecimalIntegerLiteral;

        public static readonly Parser<char, ICollection<int>> HexDigits;

        public static readonly Parser<char, int> HexDigit;

        public static readonly Parser<char, RealLiteral> RealLiteral;

        public static readonly Parser<char, ExponentPart> ExponentPart;

        public static readonly Parser<char, Sign> Sign;

        public static readonly Parser<char, RealTypeSuffix> RealTypeSuffix;

        public static readonly Parser<char, char> CharLiteral;

        public static readonly Parser<char, char> Character;

        #region SingleCharacter

        public static readonly Parser<char, char> SingleCharacter;

        private static readonly object[] SingleCharacterExpectations = new[] { "Any character except ' (U+0027), \\ (U+005C), and new-line-character" };

        private static bool IsSingleCharacter(char ch)
        {
            return (ch != '\'')
                && (ch != '\\')
                && !IsNewLineCharacter(ch);
        }

        #endregion

        public static readonly Parser<char, char> SimpleEscapeSequence;

        public static readonly Parser<char, char> HexadecimalEscapeSequence;

        public static readonly Parser<char, string> StringLiteral;

        public static readonly Parser<char, string> RegularStringLiteral;

        public static readonly Parser<char, string> RegularStringLiteralCharacters;

        public static readonly Parser<char, char> RegularStringLiteralCharacter;

        #region SingleRegularStringLiteralCharacter

        public static readonly Parser<char, char> SingleRegularStringLiteralCharacter;

        private static readonly object[] SingleRegularStringLiteralCharacterExpectations = new[] { "Any character except \" (U+0022), \\ (U+005C), and new-line-character" };

        private static bool IsSingleRegularStringLiteralCharacter(char ch)
        {
            return (ch != '\"')
                && (ch != '\\')
                && !IsNewLineCharacter(ch);
        }

        #endregion

        public static readonly Parser<char, string> VerbatimStringLiteral;

        public static readonly Parser<char, string> VerbatimStringLiteralCharacters;

        public static readonly Parser<char, char> VerbatimStringLiteralCharacter;

        public static readonly Parser<char, char> SingleVerbatimStringLiteralCharacter;

        public static readonly Parser<char, char> QuoteEscapeSequence;

        public static readonly Parser<char, string> NullLiteral;

        #endregion

        #region B.1.9 Operators and punctuators

        public static Parser<char, string> OperatorOrPunctuator;

        public static string[] OperatorsAndPunctuators = new[] {
            "{",    "}",    "[",    "]",    "(",    ")",    ".",    ",",    ":",    ";",
            "+",    "-",    "*",    "/",    "%",    "&",    "|",    "^",    "!",    "~",
            "=",    "<",    ">",    "?",    "++",   "--",   "&&",   "||",   "<<",   
            "==",   "!=",   "<=",   ">=",   "+=",   "-=",   "*=",   "/=",   "%=",   "&=",
            "|=",   "^=",   "<<=",          "->" };

        public static Parser<char, string> RightShift;

        public static Parser<char, string> RightShiftAssignment;

        #endregion

        #region B.1.10 Pre-processing directives

        public static readonly Parser<char, char> PPDirective;

        public static readonly Parser<char, string> ConditionalSymbol;

        public static readonly Parser<char, char> PPExpression;

        public static readonly Parser<char, char> PPOrExpression;

        public static readonly Parser<char, char> PPAndExpression;

        public static readonly Parser<char, char> PPEqualityExpression;

        public static readonly Parser<char, char> PPUnaryExpression;

        public static readonly Parser<char, char> PPPrimaryExpression;

        public static readonly Parser<char, char> PPDeclaration;

        public static readonly Parser<char, string> PPNewLine;

        public static readonly Parser<char, char> PPConditional;

        public static readonly Parser<char, char> PPIfSection;

        public static readonly Parser<char, char> PPElifSections;

        public static readonly Parser<char, char> PPElifSection;

        public static readonly Parser<char, char> PPElseSection;

        public static readonly Parser<char, char> PPEndif;

        public static readonly Parser<char, char> ConditionalSection;

        public static readonly Parser<char, char> SkippedSection;

        public static readonly Parser<char, char> SkippedSectionPart;

        public static readonly Parser<char, char> SkippedCharacters;

        public static readonly Parser<char, char> NotNumberSign;

        public static readonly Parser<char, char> PPDiagnostic;

        public static readonly Parser<char, char> PPMessage;

        public static readonly Parser<char, char> PPRegion;

        public static readonly Parser<char, char> PPStartRegion;

        public static readonly Parser<char, char> PPEndRegion;

        public static readonly Parser<char, char> PPLine;

        public static readonly Parser<char, char> LineIndicator;

        public static readonly Parser<char, char> FileName;

        public static readonly Parser<char, char> FileNameCharacters;

        public static readonly Parser<char, char> FileNameCharacter;

        public static readonly Parser<char, char> PPPragma;

        public static readonly Parser<char, CSharp.PragmaWarningBody> PragmaBody;

        public static readonly Parser<char, CSharp.PragmaWarningBody> PragmaWarningBody;

        public static readonly Parser<char, CSharp.WarningAction> WarningAction;

        public static readonly Parser<char, ICollection<int>> WarningList;

        #endregion

        private static Parser<char, char> CharacterOrUnicodeEscapeSequence(Func<char, bool> predicate, string label)
        {
            return state =>
            {
                if (!state.Eof)
                {
                    var current = state.Current;

                    if (predicate(current))
                    {
                        state.Next();
                        return current;
                    } // no need to switch to the failed state cause TrySatisfy will do it on fail.
                }

                return UnicodeEscapeSequence.TryValidate(predicate, label)(state);
            };
        }

        private static readonly ICollection<int> EmptyDigitsCollection;

        private static readonly ExponentPart EmptyExponentPart;
    }
}